package indi.zjh.dp.startup;

import java.io.File;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.DocumentException;

import com.netflix.curator.RetryPolicy;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.CuratorFrameworkFactory;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache;
import com.netflix.curator.framework.recipes.cache.PathChildrenCache.StartMode;
import com.netflix.curator.framework.recipes.cache.NodeCache;
import com.netflix.curator.framework.recipes.cache.NodeCacheListener;
import com.netflix.curator.framework.recipes.cache.PathChildrenCacheEvent;
import com.netflix.curator.framework.recipes.cache.PathChildrenCacheListener;
import com.netflix.curator.framework.state.ConnectionState;
import com.netflix.curator.framework.state.ConnectionStateListener;
import com.netflix.curator.retry.ExponentialBackoffRetry;

import indi.zjh.dp.access.HttpServer;
import indi.zjh.dp.db.Configuration;
import indi.zjh.dp.db.Session;
import indi.zjh.dp.error.UnsuportedFieldTypeChangeException;
import indi.zjh.dp.mng.ServerConnectionStateListener;
import indi.zjh.dp.orm.MethodFactory;
import indi.zjh.dp.orm.RelationClass;
import indi.zjh.dp.util.JSONUtil;
import indi.zjh.dp.util.RedisUtil;
import indi.zjh.dp.util.SerializeUtil;
import indi.zjh.dp.util.XMLUtil;
import indi.zjh.dp.vc.Commit;
import indi.zjh.dp.vc.Version;
import indi.zjh.dp.vc.VersionControl;
import redis.clients.jedis.JedisCommands;

public class Application implements ConnectionStateListener{
	
	public static final int SINGLE_MODE = 0;
	public static final int CLUSTER_MODE = 1;
	
	private static Logger logger = Logger.getLogger(Application.class);
	private static Application instance = null;
	private String managerNodeName = "";
	private String businessNodeName = "";
	private CuratorFramework client = null;
	private int mode = 0;
	
	class HeartbeatReport extends Thread
	{
		private Logger logger;
		private CuratorFramework client = null;
		private String businessNodeName;
		public HeartbeatReport(CuratorFramework c, String busiNodeName, Logger logger)
		{
			this.client = c;
			this.businessNodeName = busiNodeName;
			this.logger = logger;
		}
		public void run()
		{
			try
			{
				while(true)
				{
					Thread.sleep(5000);
					ServerStatus.getInstance().heartbeat();
					this.logger.info("发送心跳="+this.businessNodeName);
					byte[] data = JSONUtil.toString(ServerStatus.getInstance()).getBytes();//SerializeUtil.serialize(ServerStatus.getInstance());
					this.logger.info("业务节点发送心跳内容:"+JSONUtil.toString(ServerStatus.getInstance()));
					client.setData().forPath(this.businessNodeName, data);
				}
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 私有构造函数，单例
	 */
	private Application()
	{
	}

	/**
	 * 获取程序单例实例
	 * @return
	 */
	public static Application getInstance()
	{
		if(instance == null)
		{
			instance = new Application();
		}
		return instance;
	}
	
	/**
	 * 程序初始化， 要解决初始化异常
	 */
	
	public void init()
	{
		try
		{
			logger.info("业务节点启动, 初始化开始");
			logger.info("业务节点监听端口"+Configuration.getInstance().getHttpConf().port);

			// 当前版本
			Version currVersion = new Version();
			Configuration.getInstance().setVersion(currVersion);
			Configuration.getInstance().getVersion().analyzeClassRelation();
			
			// 获取历史版本列表
			String checkoutPath = Configuration.getInstance().getBusiConf().checkoutPath + File.separator + "checkout";
			List<Commit> commits = VersionControl.getInstance().getCommitList(3);
			
			// 如果是第一个版本
			if(commits.size() == 0)
			{
				logger.info("当前版本是第一个初始化版本");
				// 提交当前版本
				Commit commit = new Commit();
				commit.submit("V" + new Date().getTime());
				
				// 初始化
				Configuration.getInstance().getVersion().initial();
				
			}
			// 如果有历史版本
			else
			{
				logger.info("当前版本不是第一个初始化版本");
				// 检出上一个历史版本
				Version lastVersion = commits.get(0).checkout(checkoutPath + File.separator + commits.get(0).getDesc(), true);
				List<RelationClass> different = VersionControl.getInstance().compareVersion(lastVersion, currVersion);
				
				// 业务没有发送变化
				if(different.size() == 0)
				{
					logger.info("业务版本没有发生变化");
				}
				// 发生变化
				else
				{
					logger.info("提交当前新的业务版本");
					Commit commit = new Commit();
					commit.submit("V" + new Date().getTime());
				}
			}
			this.managerNodeName = System.getProperty("Manager.NodeName");
			this.businessNodeName = System.getProperty("Business.NodeName");
			
			// 集群模式
			if(this.managerNodeName != null && this.businessNodeName != null && this.managerNodeName.length() > 0 && this.businessNodeName.length() > 0)
			{
				logger.info("业务节点以集群模式启动");
				this.mode = CLUSTER_MODE;
				this.managed_init();
			}
			else
			{
				logger.info("业务节点以单机模式启动");
				this.mode = SINGLE_MODE;
			}
		}
		catch(UnsuportedFieldTypeChangeException e)
		{
			logger.error(e.getMessage());
			System.exit(1);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	
	/**
	 * 程序运行
	 * @throws Exception 
	 * @throws DocumentException 
	 * @throws SQLException 
	 */
	public void run() throws SQLException, DocumentException, Exception
	{
		logger.info("业务节点运行, 监听端口 "+Configuration.getInstance().getHttpConf().port);
		//Configuration.getInstance().getVersion().run();
		HttpServer server = new HttpServer();
		server.start(Configuration.getInstance().getHttpConf().port);
		
		
	}
	

	private void managed_init()
	{
		
		logger.info("managerNodeName="+managerNodeName+",businessNodeName="+businessNodeName);

		try
		{
			// 服务器启动
			logger.info("业务节点"+businessNodeName+"启动");
			String hostList = Configuration.getInstance().getZkConf().host;
			int sessionTimeout = Configuration.getInstance().getZkConf().sessionTimeout;
			int connectTimeout = Configuration.getInstance().getZkConf().connectTimeout;
			int reconnectTimeout = Configuration.getInstance().getZkConf().reconnectTimeout;
			int reconnectTryCount = Configuration.getInstance().getZkConf().reconnectTryCount;
	
			
			// 重连策略
			RetryPolicy retryPolicy = new ExponentialBackoffRetry(reconnectTimeout, reconnectTryCount);
						
			// 连接
			client = CuratorFrameworkFactory.newClient(hostList, sessionTimeout, connectTimeout, retryPolicy);
			client.start();
			
			// 注册状态变化事件监听
			client.getConnectionStateListenable().addListener(this);
			
//			NodeCache cache = new NodeCache(client, "/test1");
//			cache.start();
//			cache.getListenable().addListener(new NodeCacheListener(){
//
//				@Override
//				public void nodeChanged() throws Exception {
//					// TODO Auto-generated method stub
//					System.out.println("nodeChanged, data = "+new String(cache.getCurrentData().getData()));
//				}
//				
//			});
			

			final PathChildrenCache childrenCache = new PathChildrenCache(client, "/manager_node", true);
		    childrenCache.start();
		    childrenCache.getListenable().addListener(
		      new PathChildrenCacheListener() {
		        @Override
		        public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
		            switch (event.getType()) {
		            case CHILD_ADDED:
		            	logger.info("管理节点CHILD_ADDED");
		            	System.out.println("CHILD_ADDED: " + event.getData().getPath());
		            	break;
		            case CHILD_REMOVED:
		            	logger.info("管理节点CHILD_REMOVED = "+event.getData().getPath());
		            	logger.info("Application.this.managerNodeName="+Application.this.managerNodeName);
		            	if(event.getData().getPath().equals(Application.this.managerNodeName))
		            	{
		            		logger.info("管理节点退出, 业务结点随即退出");
		            		System.exit(1);
		            	}
		            	//logger.info("管理节点退出, 业务结点随即退出");
		            	//childrenCache.close();
		            	//System.exit(1);
		              
		            	break;
		            case CHILD_UPDATED:
		            	logger.info("管理节点CHILD_UPDATED");
		            	System.out.println("CHILD_UPDATED: " + event.getData().getPath());
		            	//logger.info("管理节点CHILD_UPDATED");
		              break;
		            default:
		              break;
		          }
		        }
		      }
		    );
		    
		    // 发送心跳
		    new HeartbeatReport(this.client, this.businessNodeName, logger).start();
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		
	}

	@Override
	public void stateChanged(CuratorFramework arg0, ConnectionState arg1) {
		// TODO Auto-generated method stub
		
	}
	
	public CuratorFramework getZookeeperClient()
	{
		return this.client;
	}
	
	public int getMode()
	{
		return this.mode;
	}
}
